package main;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Vector;
import java.util.Hashtable;

import main.constraint.XDF;

import automata.Alphabet;
import automata.Fsa;
import automata.Label;
import automata.State;
import automata.StateSet;

@SuppressWarnings("unchecked")

public class ConfigurationSet extends LinkedHashSet<Configuration>  implements Cloneable
{
	private static final long serialVersionUID = 1L;
	private String ref;
	private XDF kconf;
	//private String position;

	ConfigurationSet(){}
	
	ConfigurationSet(ArrayList<Configuration> a)
	{
		super.addAll(a);
	}
		
	public static ConfigurationSet empty()
	{
		return new ConfigurationSet();
	}

	public boolean add(Configuration c)
	{
		return super.add(c);
	}

	public static ConfigurationSet singleton(Configuration c)
	{
		ConfigurationSet cs = new ConfigurationSet();
		cs.add(c);
		return cs;
	}

	/**
	 * Checks if there's automaton-changing
	 * @param l label 
	 * @return
	 */
	public ConfigurationSet changeConfiguration(Label l)
	{
		ConfigurationSet cs = new ConfigurationSet();
		
		for (Iterator<Configuration> i = this.iterator(); i.hasNext();)
		{
			Configuration c = (Configuration) i.next();
			Fsa f = c.getFsa();//pego o automato 
			//System.out.println("get automato FSA");
			State sf = c.getState();//estado atual
			//System.out.println("get estado atual");
			XDF k = ((XDF) c.getKey());//pega a restricao
			//System.out.println("get restricao");
			
			//make transitions 
			addConfiguration(sf, cs, l, f, k);
			//System.out.println("make transistions of label ${"+ l.toString());
			
			//verify final state
			if (f.get_final_states().contains(sf))
			{
				int fposition = k.getFsaPosition(f);
				//System.out.println("contex position");
				//verify context position
				if (fposition == 1)
				{
					//System.out.println("walks in left side automaton");
					//walks in left side automaton
					f = k.getSourceFsa();
					sf = f.get_start_state();
					addConfiguration(sf, cs, l, f, k);
					
					//System.out.println("walks in right side automaton");
					//walks in right side automaton
					f = k.getTargetFsa();
					sf = f.get_start_state();
					addConfiguration(sf, cs, l, f, k);
				}
			}
		}
		
		KeysVal kv = new KeysVal();
		kv.printConfigurationSet(cs);
		//  AQUI TEMOS QUE ORDENAR O CONJUNTO DE CONFIGURACOES - EM CS 
		//  PRIMEIRO: AUTOMATO CONTEXTO, AUTOMATO ESQUERDA, AUTOMATO DIREITO
		//                     ORDENAR POR ESTADO        
		//
		ArrayList<Configuration> a = new ArrayList<Configuration>(cs);
		Collections.sort(a);			
		ConfigurationSet csNew = new ConfigurationSet(a);		
		return csNew;
		
	}
	
	//creates a new configuration and inserts it set configuration
	public void addConfiguration(State sf, ConfigurationSet cs, 
								Label l, Fsa f, XDF k)
	{
		//System.out.println("creates a new configuration and inserts it set configuration");
		Alphabet a = f.get_alphabet();
		Label star = new Label("*");
		State s;
		StateSet ss;
		Iterator<State> i;
		
		if (a.contains(l))
		{
			ss = f.get_dest_states(sf, l);
			for (i = ss.iterator(); i.hasNext();)
			{
				s = (State) i.next();
				cs.add(new Configuration(s, f, k));
			}
		}
		
		if (a.contains(star))
		{
			ss = f.get_dest_states(sf, star);
			for (i = ss.iterator(); i.hasNext();)
			{
				s = (State) i.next();
				Configuration c = new Configuration(s, f, k);
				if (!cs.contains(c))
					cs.add(c);
			}
		}
	}

	public Vector<SyntAttributes> keyValidationData(Vector<SyntAttributes> syntVec, int k, String data, String posAnt, 
			                        				String posAt, String nodeName,
			                        				Hashtable<String,ArrayList<Inters>> htIntersXfd) 
	{
		// Validation for leaf nodes, considering their ancestor node.
		Configuration conf;
		SyntAttributes syntAttAnt = new SyntAttributes();
		SyntAttributes syntAtt = new SyntAttributes();
		syntAtt.setNodeName(nodeName);
		syntAttAnt = (SyntAttributes) syntVec.elementAt(k);
		
		for (Iterator<Configuration> it = this.iterator(); it.hasNext();)
		{
			conf = (Configuration) it.next();
			syntAtt = conf.validateData(syntAtt, data, posAt, nodeName,htIntersXfd);
			
			if(!syntAtt.isEmpty())
			{
				  if (!syntAttAnt.isEmpty())
				  {  
				         syntAttAnt.join(syntAtt,htIntersXfd,posAnt);
				         syntVec.setElementAt(syntAttAnt,k);
				  }       
				  else 
					  syntVec.setElementAt(syntAtt,k);	  
			}
			else 
				syntVec.setElementAt(syntAttAnt,k);
		}
		
		//System.out.println("Return SyntVec: "+syntVec.toString());
		return syntVec;
	}
	
	/**
	 * This function make validation key in Node
	 * @param syntVecAnt is Vector<SyntAttributes>
	 * @param k is integer
	 * @param syntAtt is SyntAttributes 
	 * @param posAnt is String
	 * @param posAt is String
	 * @param nodeName is String
	 * @param htContextXfd is HashTable
	 * @param htIntersXfd is HashTable<String,ArrayList<Inters>> 
	 * @return Vector SyntAttributes
	 */
	public Vector<SyntAttributes> keyValidationInNode(Vector<SyntAttributes> syntVecAnt, int k, SyntAttributes syntAtt,
			                          				  String posAnt, String posAt, String nodeName, Hashtable htContextXfd, 
			                          				  Hashtable<String,ArrayList<Inters>> htIntersXfd, Hashtable<String, ArrayList<String>> hterror)
	{
		Configuration conf;
		
		SyntAttributes syntAttAnt = (SyntAttributes) syntVecAnt.elementAt(k);  //Attributes from parent node, for the same xdf 
		
		for (Iterator<Configuration> it = this.iterator(); it.hasNext();)
		{
			SyntAttributes syntAttAux = new SyntAttributes();
			conf = (Configuration) it.next();
			//System.out.println("Configuration: "+conf.toString());
			
			syntAttAux = conf.validateInNode(syntAtt, syntAttAnt, posAnt, posAt, nodeName, htContextXfd, htIntersXfd, hterror);
			
			if(!syntAttAux.isEmpty())
			{
				syntVecAnt.setElementAt(syntAttAux, k);
			}	
		}
		
		return syntVecAnt;
	}
	
	public SyntAttributes keyValidation(SyntAttributes sa, Vector<?> v,
										KeysExecution ke, XDF kc, int posk,	String p, String tagname)
	{
		SyntAttributes saNew = new SyntAttributes();
		int roleConf;

		if (!this.isEmpty())
		{
			for (Iterator<Configuration> i = this.iterator(); i.hasNext();)
			{
				Configuration c = (Configuration) i.next();
				roleConf = Role(c);
				
				saNew.addAll(keyValidationRole(sa, v, ke, kc, roleConf, posk, p, tagname));
			}
		}
		
		return saNew;
	}

	public int Role(Configuration c)
	{
		Fsa f = c.getFsa();
		State sf = c.getState();
		StateSet setF = f.get_final_states();
		XDF k = ((XDF) c.getKey());
		kconf = k;
		int fposition = k.getFsaPosition(f);
		
		if (fposition == 3)//T"
		{
			if (setF.contains(sf))
				return 1; 
			else
				return 3;
		}
		
		if (fposition == 2)//T'
		{
			if (setF.contains(sf))
				return 2;
			else
				return 4;
		}
		
		if (fposition == 1)//M
		{
			if (setF.contains(sf))
				return 5;
			else
				return 6;
		}
		
		return 0;
	}

	public SyntAttributes keyValidationRole(SyntAttributes sa, Vector v,
											KeysExecution ke, XDF kc, int role, int posk,
											String p, String tagname)
	{	
		SyntAttributes syntAtt = new SyntAttributes();
		int refCount;

		if (role == 1)
		{
			// the node is the context node of a key
			// remove all the lists from the list and check
			// Synt Attributes Part
			ArrayList al3 = new ArrayList();
			ArrayList al2 = new ArrayList();

			for (Iterator n = sa.getTargetList().iterator(); n.hasNext();)
			{
				try
				{
					al3 = (ArrayList) n.next();
					al2.add(al3);	
				}
				catch (Exception e) {}
			}

			ArrayList al1 = (ArrayList) al2.clone();
			for (Iterator j = al2.iterator(); j.hasNext();)
			{
				al3 = (ArrayList) j.next();
				if (!al1.isEmpty())
				{
					al1.remove(al3);
					if (al1.contains(al3))
					{
						syntAtt.addContextList("f");
						String kn = kc.getNameConstraint();
						//Initial.ErrorMessage += "Duplicated value "
						//		+ al3.toString() + " for key " + kn + " \n";
						//System.out.println("ERRO :"
						//		+ Initial.ErrorMessage);
					}
				}
			}
			
			if (!syntAtt.containsContextList("f"))
				syntAtt.addContextList("t");
			else
			{
				syntAtt.clearContextList();
				syntAtt.addContextList("f");
			}
			
			return syntAtt;
		}

		if (role == 2)
		{
			// the node is the target node of the key
			// Synt Attributes Part
			ArrayList<String> al2 = new ArrayList<String>();
			al2.add("NULL");
			if (sa.containsKeyList("NULL"))
			{
				syntAtt.addTargetList(al2);
			}
			else
			{
				int n = kconf.getNPaths();
				
				if (sa.sizeKeyList() != n)
					syntAtt.addTargetList(al2);
				else
					syntAtt.addTargetList(sa.getKeyList());
			}
			
			return syntAtt;
		}

		if (role == 3)
		{
			// the node is part of a key
			// SyntAtt Part
			for (Iterator<String> j = sa.getKeyList().iterator(); j.hasNext();)
				syntAtt.addKeyList((String) j.next());
			
			return syntAtt;
		}

		if (role == 4)
		{
			ArrayList al3 = new ArrayList();
			ArrayList al2 = new ArrayList();

			SyntAttributes syntAtt1 = new SyntAttributes();
			ArrayList<XDF> al4 = (ArrayList<XDF>) ke.getKeysSet();

			for (Iterator n = sa.getTargetList().iterator(); n.hasNext();)
			{
				try
				{
					al3 = (ArrayList) n.next();
					al2.add(al3);
				}
				catch (Exception e)
				{
					System.out.println("Error at context for a foreign key :"+ e);
				}
			}

			for (int j = 0; j < v.size(); j++)
			{
				XDF k = (XDF) al4.get(j);
				String kn = k.getNameConstraint();
				
				if (kn.equals(ref))
				{
					syntAtt1 = (SyntAttributes) v.elementAt(j);
				}
			}

			 //System.out.println("XDF List: "+syntAtt1.toString());

			for (Iterator n = syntAtt1.getTargetList().iterator(); n.hasNext();)
			{
				try
				{
					al3 = (ArrayList) n.next();
					refCount = 0;
					
					while (al2.contains(al3))
					{
						refCount++;
						al2.remove(al3);
					}
				}
				catch (Exception e)
				{
					//System.out.println("Error in FK validation");
				}
			}

			// Here we verify if the foreign key is respected in this context.
			if (al2.isEmpty())
				syntAtt.addContextList("t");
			else
				syntAtt.addContextList("f");
		}
		
		// For Paths:
		if (role == 5)// path for key components
		{ 
			syntAtt.addAllKey(sa);
		}

		if (role == 6)// path for target
		{
			syntAtt.addAllTarget(sa);
		}
		
		if (role == 8)// path for context
		{
			syntAtt.addAllContext(sa);
		}
		
		return syntAtt;
	}
	
	public XDF getXDF()
	{
		return kconf;
	}
	
	public String toString()
	{
		String str = new String();
		
		for (Iterator<Configuration> i = this.iterator(); i.hasNext();)
		{
			Configuration c = (Configuration) i.next();
			str += c.toString();
			str += ",";
		}
		
		return str;
	}

}
